package cn.outsourcing.supermarket.service.impl;

import cn.outsourcing.supermarket.bo.HiolabsGoodsSpecificationBO;
import cn.outsourcing.supermarket.common.constants.CommonConstants;
import cn.outsourcing.supermarket.common.exception.SupermarketException;
import cn.outsourcing.supermarket.common.utils.DataUtils;
import cn.outsourcing.supermarket.common.utils.JwtUtils;
import cn.outsourcing.supermarket.common.utils.StrUtils;
import cn.outsourcing.supermarket.converter.HiolabsCartConverter;
import cn.outsourcing.supermarket.converter.HiolabsOrderConverter;
import cn.outsourcing.supermarket.dto.AddCartDTO;
import cn.outsourcing.supermarket.dto.CartCheckedDTO;
import cn.outsourcing.supermarket.mapper.HiolabsCartMapper;
import cn.outsourcing.supermarket.mapper.HiolabsProductMapper;
import cn.outsourcing.supermarket.po.*;
import cn.outsourcing.supermarket.service.*;
import cn.outsourcing.supermarket.vo.AddCartVO;
import cn.outsourcing.supermarket.vo.OrderGoodsVO;
import cn.outsourcing.supermarket.vo.PayCartNotAddressVO;
import cn.outsourcing.supermarket.vo.PayCartVO;
import cn.outsourcing.supermarket.vo.cart.AddCartListVO;
import cn.outsourcing.supermarket.vo.cart.AddCartTotalVO;
import cn.outsourcing.supermarket.vo.hiolabs.HiolabsAddressVO;
import cn.outsourcing.supermarket.vo.hiolabs.HiolabsCartVO;
import cn.outsourcing.supermarket.vo.hiolabs.HiolabsOrderVO;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author gzkemays
 * @since 2021-10-14
 */
@Service
public class HiolabsCartServiceImpl extends ServiceImpl<HiolabsCartMapper, HiolabsCart>
    implements HiolabsCartService {
  @Resource HiolabsCartMapper cartMapper;
  @Resource HiolabsUserService userService;
  @Resource HiolabsProductMapper productMapper;
  @Resource HiolabsAddressService addressService;
  @Resource HiolabsOrderService hiolabsOrderService;
  @Resource HiolabsOrderGoodsService hiolabsOrderGoodsService;
  @Resource HttpServletRequest request;

  @Override
  public Map<String, Integer> getCartTotal() {
    Map<String, Integer> map = new HashMap<>(1);
    List<HiolabsCart> cartList =
        cartMapper.selectList(
            Wrappers.<HiolabsCart>lambdaQuery()
                .eq(
                    HiolabsCart::getUserId,
                    userService.getUserMsg(JwtUtils.getTokenByRequest(request)).getId())
                .eq(HiolabsCart::getIsDelete, 0));
    Integer count = 0;
    updateFast();
    for (HiolabsCart cart : cartList) {
      count += cart.getNumber();
    }
    map.put("goodsCount", count);
    return map;
  }

  @Override
  public AddCartVO add(AddCartDTO entity) {
    Integer userId =
        (Integer) JwtUtils.getTokenClaimByRequest(request, CommonConstants.TOKEN_USER_ID_KEY);
    int index = updateCart(entity, userId);
    // 更新或插入成功
    if (index > 0) {
      return checkedCart(userId);
    }
    throw new SupermarketException(CommonConstants.SQL_FAIL_CODE, "数据库操作异常");
  }

  @Override
  public AddCartVO updateByEntity(AddCartDTO entity) {
    Integer userId =
        (Integer) JwtUtils.getTokenClaimByRequest(request, CommonConstants.TOKEN_USER_ID_KEY);
    HiolabsCart cart = cartMapper.selectById(entity.getId());
    cart.setNumber(entity.getNumber());
    cart.setAddTime(DataUtils.getSeconds());
    cartMapper.updateById(cart);
    return checkedCart(userId);
  }

  @Override
  public AddCartVO index() {
    Integer userId =
        (Integer) JwtUtils.getTokenClaimByRequest(request, CommonConstants.TOKEN_USER_ID_KEY);
    return checkedCart(userId);
  }

  @Override
  public HiolabsOrderVO submitOrder(Set<Integer> cartIds, Boolean isIntegral, Integer channel) {
    if (CollectionUtils.isEmpty(cartIds)) {
      throw new SupermarketException(CommonConstants.REQUEST_FAIL_CODE, "请求参数异常");
    }
    HiolabsUser userMsg = userService.getUserMsg(JwtUtils.getTokenByRequest(request));
    List<HiolabsCart> hiolabsCarts = listByIds(cartIds);
    HiolabsOrder hiolabsOrder =
        HiolabsOrder.creatOrderByCart(hiolabsCarts, userMsg, isIntegral, channel);
    String name = Optional.of(userMsg.getName()).orElse("");
    if (StringUtils.isBlank(name)) {
      hiolabsOrder.setConsignee(StrUtils.getStrByBase64(userMsg.getNickname()));
    } else {
      hiolabsOrder.setConsignee(userMsg.getName());
    }
    boolean save = hiolabsOrderService.save(hiolabsOrder);
    List<HiolabsOrderGoods> hiolabsOrderGoods =
        HiolabsOrder.creatOrderDedailByCart(hiolabsCarts, hiolabsOrder);
    if (CollectionUtils.isNotEmpty(hiolabsOrderGoods)) {
      hiolabsOrderGoodsService.saveBatch(hiolabsOrderGoods);
    }
    // FIXME：订单交易成功后回调，删除购物车。【此处采用了逻辑删除时的直接调用】,关闭逻辑删除时采用update方式
    //    removeByIds(cartIds);
    for (HiolabsCart hiolabsCart : hiolabsCarts) {
      hiolabsCart.setIsDelete(1);
    }
    updateBatchById(hiolabsCarts);

    if (save) {
      return HiolabsOrderConverter.INSTANCE.convertToVo(hiolabsOrder);
    }
    throw new SupermarketException(CommonConstants.RESPONSE_FAIL_CODE, "订单存储异常");
  }

  @Override
  public HiolabsOrderVO submitOrderByBalance(BigDecimal balance) {
    if (Objects.isNull(balance)) {
      throw new SupermarketException(CommonConstants.REQUEST_FAIL_CODE, "请求参数异常");
    }
    HiolabsUser userMsg = userService.getUserMsg(JwtUtils.getTokenByRequest(request));

    HiolabsOrder hiolabsOrder = HiolabsOrder.creatOrderByBalance(balance, userMsg);
    String name = Optional.of(userMsg.getName()).orElse("");
    if (StringUtils.isBlank(name)) {
      hiolabsOrder.setConsignee(StrUtils.getStrByBase64(userMsg.getNickname()));
    } else {
      hiolabsOrder.setConsignee(userMsg.getName());
    }
    boolean save = hiolabsOrderService.save(hiolabsOrder);
    if (save) {
      return HiolabsOrderConverter.INSTANCE.convertToVo(hiolabsOrder);
    }
    throw new SupermarketException(CommonConstants.RESPONSE_FAIL_CODE, "订单存储异常");
  }

  @Override
  public PayCartVO checkedCart(CartCheckedDTO entity) {
    Integer userId =
        (Integer) JwtUtils.getTokenClaimByRequest(request, CommonConstants.TOKEN_USER_ID_KEY);
    // 先默认运费为0
    int freightPrice = 0;
    // 商品总价格，运费为0，商品总价格等于订单总价格。
    double goodsTotalPrice = 0;
    // 订单总价格
    double orderTotalPrice = 0;
    // 实付价格 -- 扣除优惠卷积分兑换等……
    double actualPrice = 0;
    // 商品数量
    int goodsCount = 0;
    // 是否缺货
    int outStock = 0;
    // ？
    int numberChange = 0;
    // 商品Str价格
    String goodPrice;
    // 购物车商品
    List<AddCartListVO> addCartList = new ArrayList<>();
    // 地址列表
    HiolabsAddressVO addressByIdGroup;
    // 是否从订单中获取
    String orderFrom = entity.getOrderFrom();
    // 获取地址 id
    Integer addressId = entity.getAddressId();
    // 用户信息
    HiolabsUser userMsg = userService.getUserMsg(JwtUtils.getTokenByRequest(request));
    // 最大使用积分
    BigDecimal maxIntegral;
    // 积分金额
    BigDecimal priceByIntegral;
    // 从购物车或者立即购买时执行
    if ("undefined".equals(orderFrom)) {
      addCartList =
          cartMapper.getAddCartList(userId).stream()
              .filter(cart -> cart.getChecked() == 1)
              .filter(cart -> cart.getIsFast().equals(entity.getAddType()))
              .collect(Collectors.toList());
    } else {
      // 寻找相同而且 is_delete = 1 的数据，范围查询过滤去重获取对应的数据。
      List<OrderGoodsVO> orderGoods =
          hiolabsOrderGoodsService.getOrderGoods(Integer.parseInt(orderFrom));
      Set<Integer> productIds =
          orderGoods.stream().map(OrderGoodsVO::getProductId).collect(Collectors.toSet());
      Set<Integer> goodsNumbers =
          orderGoods.stream().map(OrderGoodsVO::getNumber).collect(Collectors.toSet());
      Set<HiolabsCart> carts =
          new HashSet<>(
              cartMapper.selectList(
                  Wrappers.<HiolabsCart>lambdaQuery()
                      .in(HiolabsCart::getProductId, productIds)
                      .in(HiolabsCart::getNumber, goodsNumbers)
                      .eq(HiolabsCart::getIsDelete, 1)));
      for (OrderGoodsVO orderGood : orderGoods) {
        String ogSpeIds = orderGood.getGoodsSpecifitionIds();
        Integer ogProductId = orderGood.getProductId();
        Integer ogUserId = orderGood.getUserId();
        Integer ogNumber = orderGood.getNumber();
        for (HiolabsCart cart : carts) {
          String cartSpeIds = cart.getGoodsSpecifitionIds();
          Integer cartProductId = cart.getProductId();
          Integer cartUserId = cart.getUserId();
          Integer cartNumber = cart.getNumber();
          if (ogSpeIds.equals(cartSpeIds)
              && ogProductId.equals(cartProductId)
              && ogUserId.equals(cartUserId)
              && ogNumber.equals(cartNumber)) {
            addCartList.add(HiolabsCartConverter.INSTANCE.convertToAddCartVo(cart));
          }
        }
      }
    }
    addCartList =
        addCartList.stream()
            .filter(DataUtils.distinctByKey(AddCartListVO::getProductId))
            .collect(Collectors.toList());
    // 遍历计算商品数据
    for (AddCartListVO cartListVO : addCartList) {
      // 此处暂且仅计算商品价格
      Integer number = cartListVO.getNumber();
      goodsTotalPrice += number * cartListVO.getRetailPrice().doubleValue();
      goodsCount += number;
      // 如果某个商品缺货了，表明为缺货，即不能支付。
      if (cartListVO.getNumber() == 0) {
        outStock = 1;
      }
    }
    maxIntegral = DataUtils.getMaxIntegral(new BigDecimal(goodsTotalPrice), userMsg.getCredits());
    priceByIntegral = DataUtils.getPriceByIntegral(maxIntegral);
    // 使用积分
    if (entity.getIsIntegral()) {
      // 实际价格 = 商品价格 - 积分金额 + 运费
      BigDecimal subtract =
          new BigDecimal(goodsTotalPrice)
              .subtract(DataUtils.getPriceByIntegral(maxIntegral))
              .add(new BigDecimal(freightPrice));
      actualPrice = subtract.doubleValue();
    } else {
      actualPrice = goodsTotalPrice + freightPrice;
    }
    goodPrice = String.valueOf(goodsTotalPrice);
    // 如果没配送地址则返回 PayCartNotAddressVO 对象
    if (addressId.equals(0)) {
      PayCartNotAddressVO notAddress = new PayCartNotAddressVO();
      // 表明地址为 0 时返回的参数指向 status
      notAddress.setCheckedAddressStatus(0);
      notAddress.setFreightPrice(freightPrice);
      notAddress.setCheckedGoodsList(addCartList);
      notAddress.setGoodsTotalPrice(goodPrice);
      notAddress.setOrderTotalPrice(goodPrice);
      // 先不计优惠卷、积分等折扣那么它就是订单的总价格
      notAddress.setActualPrice(String.valueOf(actualPrice));
      notAddress.setGoodsCount(goodsCount);
      notAddress.setOutStock(outStock);
      notAddress.setNumberChange(numberChange);
      notAddress.setIntegral(maxIntegral);
      notAddress.setIntegralPay(priceByIntegral);
      return notAddress;
    }
    // 如果有配送地址，将配送信息进行整合返回。
    addressByIdGroup = addressService.getAddressByIdGroup(entity.getAddressId());
    return PayCartVO.builder()
        .checkedAddress(addressByIdGroup)
        .freightPrice(freightPrice)
        .checkedGoodsList(addCartList)
        .goodsTotalPrice(goodPrice)
        .orderTotalPrice(goodPrice)
        .actualPrice(goodPrice)
        .goodsCount(goodsCount)
        .outStock(outStock)
        .numberChange(numberChange)
        .integral(maxIntegral)
        .integralPay(priceByIntegral)
        .build();
  }

  @Override
  public AddCartVO deleteGoods(Integer productId) {
    // 根据 product id 进行删除购物车商品
    int delete =
        cartMapper.update(
            null,
            Wrappers.<HiolabsCart>lambdaUpdate()
                .eq(HiolabsCart::getProductId, productId)
                .set(HiolabsCart::getIsDelete, 1));
    if (delete > 0) {
      return index();
    }
    throw new SupermarketException(CommonConstants.SQL_FAIL_CODE, "删除失败");
  }

  @Override
  public AddCartVO checkedGoods(Map<String, Object> entity) {
    Integer userId =
        (Integer) JwtUtils.getTokenClaimByRequest(request, CommonConstants.TOKEN_USER_ID_KEY);
    List<Integer> ids;
    String productIds = String.valueOf(entity.get("productIds"));
    if (productIds.contains(",")) {
      List<String> strings = Arrays.asList(productIds.split(","));
      ids = strings.stream().map(Integer::parseInt).collect(Collectors.toList());
    } else {
      ids =
          new ArrayList<Integer>(1) {
            {
              add(Integer.parseInt(productIds));
            }
          };
    }
    int update =
        cartMapper.update(
            null,
            Wrappers.<HiolabsCart>lambdaUpdate()
                .in(HiolabsCart::getProductId, ids)
                .eq(HiolabsCart::getUserId, userId)
                .set(HiolabsCart::getChecked, entity.get("isChecked")));
    if (update > 0) {
      return index();
    }
    throw new SupermarketException(CommonConstants.SQL_FAIL_CODE, "数据库操作失败");
  }

  @Override
  public List<HiolabsCartVO> getOrderGoods() {
    Integer userId =
        (Integer) JwtUtils.getTokenClaimByRequest(request, CommonConstants.TOKEN_USER_ID_KEY);
    return HiolabsCartConverter.INSTANCE.convertToVoList(
        cartMapper.selectList(
            Wrappers.<HiolabsCart>lambdaQuery()
                .eq(HiolabsCart::getUserId, userId)
                .eq(HiolabsCart::getIsDelete, 0)));
  }

  /**
   * 加入购物车时先对商品进行加入
   *
   * <ul>
   *   当 addType == 0，执行加入购物车操作。
   *   <li>如果商品不存在，则执行 {@link #insert(AddCartDTO, Integer)}
   *   <li>如果存在则执行 {@link #updateData(HiolabsCart)} )}
   * </ul>
   *
   * <ul>
   *   当 addType == 1，执行直接购买操作。
   *   <li>生成临时购物车订单，且让 isFast = 1。
   *   <li>isFast = 1 视为临时购物车，如果未付款退出则 isFast = 0 并在购物车寻找是否有相同的物品，如果有则进行添加操作。
   * </ul>
   *
   * @param entity 插入商品的信息
   */
  private int updateCart(AddCartDTO entity, Integer userId) {
    // 加入购物车，修复：购物车缺少判断加入购物车还是立即购买
    if (entity.getAddType().equals(0)) {
      HiolabsCart cart =
          cartMapper.selectOne(
              Wrappers.<HiolabsCart>lambdaQuery()
                  .eq(HiolabsCart::getUserId, userId)
                  .eq(HiolabsCart::getGoodsId, entity.getGoodsId())
                  .eq(HiolabsCart::getProductId, entity.getProductId())
                  .eq(HiolabsCart::getIsDelete, 0));
      if (cart == null) {
        return insert(entity, userId);
      }
      cart.setAddTime(DataUtils.getSeconds());
      cart.setNumber(cart.getNumber() + entity.getNumber());
      return updateData(cart);
    }
    // 当商品立即购买时执行，将原有的 fast 数据进行添加至购物车，并进行逻辑删除且创建新的立即购买数据。
    updateFast();
    return insert(entity, userId);
  }

  /**
   * 新增操作，默认加入
   *
   * @param entity 商品信息
   * @param userId 用户id
   */
  private int insert(AddCartDTO entity, Integer userId) {
    HiolabsProduct product = productMapper.selectById(entity.getProductId());
    HiolabsGoodsSpecificationBO goodsSpecificationBO =
        cartMapper.getGoodsSpecificationMerge(entity.getProductId());

    return cartMapper.insert(
        HiolabsCart.builder()
            .userId(userId)
            .goodsId(goodsSpecificationBO.getGoodsId())
            .goodsSn(product.getGoodsSn())
            .productId(entity.getProductId())
            .goodsName(product.getGoodsName())
            .goodsAka(goodsSpecificationBO.getValue())
            .goodsWeight(product.getGoodsWeight())
            .addPrice(product.getRetailPrice())
            .retailPrice(product.getRetailPrice())
            .number(entity.getNumber())
            .goodsSpecifitionNameValue(goodsSpecificationBO.getName())
            .goodsSpecifitionIds(String.valueOf(goodsSpecificationBO.getSpecificationMainId()))
            .listPicUrl(goodsSpecificationBO.getListPicUrl())
            .freightTemplateId(goodsSpecificationBO.getFreightTemplateId())
            .isOnSale(goodsSpecificationBO.getIsOnSale())
            .addTime(DataUtils.getSeconds())
            .isFast(entity.getAddType().equals(1))
            .build());
  }

  /**
   * 更新操作
   * <li>entity 主要更新 number，以及 addTime 时间即可。
   *
   * @param cart 传入的更新数据
   * @return 更新是否成功
   */
  private int updateData(HiolabsCart cart) {
    return cartMapper.update(
        cart,
        Wrappers.<HiolabsCart>lambdaQuery()
            .eq(HiolabsCart::getGoodsId, cart.getGoodsId())
            .eq(HiolabsCart::getUserId, cart.getUserId())
            .eq(HiolabsCart::getProductId, cart.getProductId())
            .eq(HiolabsCart::getIsDelete, 0));
  }

  /**
   * 检查购物车信息
   *
   * @param userId yoghurt ID
   * @return 返回检查结果
   */
  private AddCartVO checkedCart(Integer userId) {
    List<AddCartListVO> cartList = cartMapper.getAddCartList(userId);
    Integer goodsCount = 0;
    double goodsAmount = 0.00;
    for (AddCartListVO cartListVO : cartList) {
      goodsCount += cartListVO.getNumber();
      // 计算选中的总价钱
      if (cartListVO.getChecked() == 1) {
        goodsAmount += cartListVO.getNumber() * cartListVO.getRetailPrice().doubleValue();
      }
    }
    return AddCartVO.builder()
        .cartList(cartList)
        .cartTotal(
            AddCartTotalVO.builder()
                .goodsCount(goodsCount)
                .goodsAmount(goodsAmount)
                .checkedGoodsAmount(goodsAmount)
                .checkedGoodsCount(goodsCount)
                .userId(userId)
                .numberChange(0)
                .build())
        .build();
  }

  /**
   * 更新购物车商品状态
   * <li>当进入商品时，更新购物车状态，将 isFast=true 的商品进行逻辑删除，并且将其加入购物车当中。
   * <li>如果原购物车没有 fast=true 的商品，则过滤出来，将 fast 更新为 false
   */
  private void updateFast() {
    Integer userId =
        (Integer) JwtUtils.getTokenClaimByRequest(request, CommonConstants.TOKEN_USER_ID_KEY);
    List<HiolabsCart> cartList =
        cartMapper.selectList(
            Wrappers.<HiolabsCart>lambdaQuery()
                .eq(HiolabsCart::getUserId, userId)
                .eq(HiolabsCart::getIsDelete, 0));
    // 立即购买的数据
    List<HiolabsCart> fastCart =
        cartList.stream().filter(HiolabsCart::getIsFast).collect(Collectors.toList());
    // 原购物车的数据
    List<HiolabsCart> noFast =
        cartList.stream().filter(cart -> !cart.getIsFast()).collect(Collectors.toList());
    for (HiolabsCart fast : fastCart) {
      Integer fastProductId = fast.getProductId();
      for (HiolabsCart cart : noFast) {
        // 如果原购物车有相同的产品数据，则购物车进行添加处理。
        if (cart.getProductId().equals(fastProductId)) {
          cart.setNumber(cart.getNumber() + fast.getNumber());
        }
        // 关闭逻辑删除时的方法
        fast.setIsDelete(1);
      }
    }
    // FIXME: 利用productId列表过滤出立即购买生成的购物车【fastCart】与原购物车【noFast】的差集数据，执行效率有待商酌。
    Set<Integer> productIds =
        noFast.stream().map(HiolabsCart::getProductId).collect(Collectors.toSet());
    List<HiolabsCart> newGoods =
        fastCart.stream()
            .filter(fast -> !productIds.contains(fast.getProductId()))
            .peek(
                fast -> {
                  fast.setIsFast(false);
                  fast.setIsDelete(0);
                })
            .collect(Collectors.toList());

    // 打开逻辑删除时的方法，并且更新去掉 fastCart
    //    this.removeByIds(fastCart.stream().map(HiolabsCart::getId).collect(Collectors.toList()));
    this.updateBatchById(
        Lists.newArrayList(fastCart, noFast, newGoods).stream()
            .flatMap(Collection::stream)
            .distinct()
            .collect(Collectors.toList()));
  }
}
